﻿using Codemonk.Common.Emit;
using System;
using Codemonk.Common.Caching;
using System.Collections.Generic;
using System.Data;

namespace Codemonk.Common.Data
{
    public static class IDbAccessorExtension
    {
        private static ICacheCollection<Type, ITypeAccessor> digestCache = new DictionaryCache<Type, ITypeAccessor>();
        private static readonly object Sync = new object();
        private static ITypeAccessor GetTypeDigest(Type type)
        {
            ITypeAccessor digest = null;
            if (!digestCache.TryGet(type, out digest))
            {
                digest = new TypeAccessor(type);
                if (!digestCache.ContainsKey(type))
                {
                    lock (Sync)
                    {
                        if (!digestCache.ContainsKey(type))
                            digestCache.Set(type, digest);
                    }
                }
            }
            return digest;
        }
        public static List<T> ExecuteObjects<T>(this IDbAccessor accessor, IDataReader reader, Dictionary<String, String> mapping = null) where T : new()
        {
            if (reader == null || accessor == null) throw new NullReferenceException();
            var type = typeof(T);
            var digest = GetTypeDigest(type);
            List<T> list = new List<T>();
            var setters = digest.SetterNames;
            using (reader)
            {
                while (reader.Read())
                {
                    T obj = (T)digest.Create();
                    if (mapping != null)
                    {
                        foreach (var kv in mapping)
                        {
                            try
                            {
                                var value = reader[kv.Key];
                                digest.SetProperty(obj, kv.Value, value);
                            }
                            catch { }
                        }
                    }
                    else
                    {
                        foreach (var setter in setters)
                        {
                            try
                            {
                                var value = reader[setter];
                                digest.SetProperty(obj, setter, value);
                            }
                            catch { }
                        }
                    }
                    list.Add(obj);
                }
            }
            return list;
        }

        public static List<T> ExecuteObjects<T>(this IDbAccessor accessor, IDbCommand cmd, Dictionary<String, String> mapping = null) where T : new()
        {
            if (cmd == null) throw new NullReferenceException();
            using (cmd)
            {
                return accessor.ExecuteObjects<T>(cmd.ExecuteReader(CommandBehavior.CloseConnection));
            }
        }

        public static List<T> ExecuteObjects<T>(this IDbAccessor accessor, IDbConnection connetion, string cmdtext, Dictionary<String, String> mapping = null,
            CommandType cmdtype = CommandType.Text, CommandBehavior behaivor = CommandBehavior.Default, params IDataParameter[] paras) where T : new()
        {
            return accessor.ExecuteObjects<T>(accessor.ExecuteReader(connetion, cmdtext, cmdtype, behaivor, paras), mapping);
        }

        public static List<T> ExecuteObjects<T>(this IDbAccessor accessor, IDbConnection connection, string cmdtext,
            DbParameterBuilder builder, Dictionary<String, String> mapping = null, CommandType cmdtype = CommandType.Text, CommandBehavior behaivor = CommandBehavior.Default) where T : new()
        {
            return accessor.ExecuteObjects<T>(accessor.ExecuteReader(connection, cmdtext, cmdtype, behaivor), mapping);
        }
        public static List<T> ExecuteObjects<T, P>(this IDbAccessor accessor, IDbConnection connection, string cmdtext,
            DbParameterBuilder<P> builder, Dictionary<String, String> mapping = null, CommandType cmdtype = CommandType.Text,
            CommandBehavior behaivor = CommandBehavior.Default)
            where P : IDataParameter, new()
            where T : new()
        {
            return accessor.ExecuteObjects<T>(accessor.ExecuteReader<P>(connection, cmdtext, builder, cmdtype, behaivor), mapping);
        }
        public static List<T> ExecuteObjects<T>(this IDbAccessor accessor, string cmdtext, Dictionary<String, String> mapping = null,
            CommandType cmdtype = CommandType.Text,
            CommandBehavior behaivor = CommandBehavior.Default, params IDataParameter[] paras) where T : new()
        {
            return accessor.ExecuteObjects<T>(accessor.ExecuteReader(cmdtext, cmdtype, behaivor), mapping);
        }
        public static List<T> ExecuteObjects<T>(this IDbAccessor accessor, string cmdtext, DbParameterBuilder builder, Dictionary<String, String> mapping = null,
            CommandType cmdtype = CommandType.Text, CommandBehavior behaivor = CommandBehavior.Default) where T : new()
        {
            return accessor.ExecuteObjects<T>(accessor.ExecuteReader(cmdtext, builder, cmdtype, behaivor), mapping);
        }
        public static List<T> ExecuteObjects<T, P>(this IDbAccessor accessor, string cmdtext, DbParameterBuilder<P> builder, Dictionary<String, String> mapping = null,
            CommandType cmdtype = CommandType.Text,
            CommandBehavior behaivor = CommandBehavior.Default)
            where P : IDataParameter, new()
            where T : new()
        {
            return accessor.ExecuteObjects<T>(accessor.ExecuteReader<P>(cmdtext, builder, cmdtype, behaivor), mapping);
        }
    }
}
